#include "XmlSerializer.h"


#include <QDomAttr>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>


XmlSerializer::XmlSerializer(QObject *parent) :
    AbstractSerializer(parent)
{
}


void XmlSerializer::serialize(QObject* source, QIODevice *output)
{
    if (!output) return;

    QDomDocument doc("ItemXml"); // this is not serialized?

    QDomElement root = toXmlElem(source, doc);
    if (root.isNull()) return;

    doc.appendChild(root);

    QByteArray arr = doc.toByteArray(4);

    output->write(arr);
}


QObject* XmlSerializer::deserialize(QIODevice* input)
{
    QDomDocument doc;
    if (!doc.setContent(input)) return 0;

    QDomElement root = doc.documentElement();

    return fromXmlElem(root);
}


QDomElement XmlSerializer::toXmlElem(QObject *source, QDomDocument doc)
{
    if (!source) return QDomElement();

    const QMetaObject* metaObj = source->metaObject();

    QString tag = metaObj->className();
    QDomElement elem = doc.createElement(tag);

    // Add static properties, which are defined with "Q_PROPERTY"
    for (int index = 0; index < metaObj->propertyCount(); index++)
    {
        QMetaProperty metaProp = metaObj->property(index);
        QString attr = metaProp.name();

        if (attr.isEmpty()) continue;

        QString data = metaProp.read(source).toString();
        if (data.isEmpty()) continue;

        elem.setAttribute(attr, data);
    }

    // Add dynamic properties, which have been set with QObject::setProperty().
    foreach (QByteArray name, source->dynamicPropertyNames())
    {
        QVariant data = source->property(name);
        if (data.isNull()) continue;

        // TODO: CREATE STORAGE TYPE USING INSTRUCTION FROM SOURCE
        elem.setAttribute(name, data.toString());
    }


    // Add child items respectively
    foreach (QObject* item, source->children())
    {
        QDomElement child = toXmlElem(item, doc);
        if (child.isNull()) continue;

        elem.appendChild(child);
    }

    return elem;
}


QObject* XmlSerializer::fromXmlElem(QDomElement source, QObject* parent)
{
    void* p = QMetaType::construct(QMetaType::type(source.tagName().toAscii()));
    if (!p)
    {
        qDebug() << Q_FUNC_INFO << "Can't construct" << source.tagName();
        return 0;
    }

    QObject* item = reinterpret_cast<QObject*>(p);
    if (!item)
    {
        qDebug() << Q_FUNC_INFO << "-" << source.tagName() << "is not a QObject!";
        return 0;
    }

    item->setParent(parent);
    const QMetaObject* metaObj = item->metaObject();

    // get item content
    QDomNamedNodeMap named_nodes = source.attributes();
    for (int i = 0; i < named_nodes.count(); i++)
    {
        QDomAttr attribute = named_nodes.item(i).toAttr();
        if (attribute.isNull()) continue;

        int index = metaObj->indexOfProperty(attribute.name().toAscii());
        if (index < 0)
        {
            // dynamic property
            item->setProperty(attribute.name().toAscii(), attribute.value());
            continue;
        }

        // static property defined with Q_PROPERTY
        QMetaProperty metaProp = metaObj->property(index);
        metaProp.write(item, attribute.value());
    }

    for (QDomElement elem = source.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        QObject* child = fromXmlElem(elem, item);
        if (!child) continue;

    }

    return item;
}


